﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using RobotCtrl;


namespace RobotView
{
    public partial class WorldView : UserControl
    {
        private World world;

        private Timer updateTimer;
        private double robotRadius=0.3;
        private Image buffer;
        private Pen zeroPen = new Pen(Color.Red, 2);
        private Pen normalPen = new Pen(Color.Green, 1);
        private Pen radiusPen = new Pen(Color.Yellow, 2);
        private Pen distancePen= new Pen(Color.IndianRed, 1);
        private Pen pathPen = new Pen(Color.BlanchedAlmond, 1);
        private SolidBrush pinkBrush = new SolidBrush(Color.Pink);
        private SolidBrush greenBrush = new SolidBrush(Color.Green);
        private int zeroPointX = 0;
        private int zeroPointY = 0;
        private int gridXSize = 0;
        private int gridYSize = 0;
        private int robotX = 0;
        private int robotY = 0;
        private int oldRobotX = 0;
        private int oldRobotY = 0;
        private List<Point> pathArray;
        private int distanceThreshold = 5;

        public WorldView()
        {
            InitializeComponent();

            pathArray = new List<Point>();
            pathArray.Add(new Point(zeroPointX, zeroPointY));

            updateTimer = new Timer();
            updateTimer.Tick += new EventHandler(updateTimer_Tick);
            updateTimer.Interval = 10;
            updateTimer.Enabled = true;
        }

        private void updateTimer_Tick(object sender, EventArgs e)
        {
            updateRobot();
        }

        public WorldView(World w)
            :this()
        {
            World = w;
            calculateZeroPoint();
        }

        private void calculateZeroPoint()
        {
            gridXSize = ClientRectangle.Width / world.GridX;
            gridYSize = ClientRectangle.Height / world.GridY;
            
            zeroPointX = world.ZeroX * gridXSize;
            zeroPointY = world.ZeroY * gridYSize;

            pathArray.Clear();
            pathArray.Add(new Point(zeroPointX, zeroPointY));
        }

        public World World
        {
            get { return world; }
            set { 
                world = value; 
                this.calculateZeroPoint(); 
            }
        }

        public void reset()
        {
            clearPath();
            updateRobot();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            updateRobot();
            base.OnPaint(e);
        }
        protected override void OnPaintBackground(PaintEventArgs e)
        {
            //base.OnPaintBackground(e);
        }

        private void clearPath()
        {
            pathArray.Clear();
            pathArray.Add(new Point(zeroPointX, zeroPointY));
        }

        private void updateRobot()
        {
            if (buffer == null)
            {
                //Avoid Memory Exception
                //buffer.Dispose();
                buffer = new Bitmap(ClientRectangle.Width, ClientRectangle.Height);
            }
            
            robotX = (int)(World.Robot.Position.X * gridXSize) + zeroPointX;
            robotY = (int)((World.Robot.Position.Y * gridYSize)*-1) + zeroPointY;            

            //buffer = new Bitmap(ClientRectangle.Width, ClientRectangle.Height);
            Graphics g = Graphics.FromImage(buffer);
            g.Clear(Color.Black);
            drawObstacle(g);
            drawGrid(g);
            drawPath(g);
            drawRobot(g);

            this.CreateGraphics().DrawImage(buffer, 0, 0);
            //e.Graphics.DrawImage(buffer, 0, 0);
        }

        private void drawRobot(Graphics g)
        {
            double botAngle = World.Robot.Position.Angle;
            double radarDistance = World.Robot.Radar.Distance;
            double radarAngle = Math.Atan(World.Robot.RelativeRadarPosition.Y/World.Robot.RelativeRadarPosition.X);
            double radarRadius = Math.Sqrt(Math.Pow(World.Robot.RelativeRadarPosition.X * gridXSize, 2) + Math.Pow(World.Robot.RelativeRadarPosition.Y * gridYSize, 2));

            int radarX = (int)(robotX + radarRadius * Math.Cos((botAngle * Math.PI / 180) + radarAngle));
            int radarY = (int)(robotY - radarRadius * Math.Sin((botAngle * Math.PI / 180) + radarAngle));
            
            int obstacleX = (int)(radarX + radarDistance * gridXSize * Math.Cos(((botAngle+World.Robot.RelativeRadarPosition.Angle) * Math.PI / 180)));
            int obstacleY = (int)(radarY - radarDistance * gridYSize * Math.Sin(((botAngle + World.Robot.RelativeRadarPosition.Angle) * Math.PI / 180)));

            g.FillEllipse(greenBrush, new Rectangle(robotX - (int)(robotRadius * gridXSize), robotY - (int)(robotRadius * gridYSize), (int)(robotRadius * gridXSize) * 2, (int)(robotRadius * gridYSize) * 2));
            g.DrawLine(radiusPen, robotX, robotY, (int)(robotX + (robotRadius*gridXSize) * Math.Cos(botAngle* Math.PI / 180)), (int)(robotY - (robotRadius*gridYSize) * Math.Sin(botAngle * Math.PI / 180)));
            g.FillEllipse(pinkBrush, new Rectangle(radarX-2, radarY-2, 4, 4));
            g.DrawLine(distancePen, radarX, radarY, obstacleX, obstacleY);
        }

        private void drawPath(Graphics g)
        {
            if (Math.Abs(robotX - oldRobotX) > distanceThreshold || Math.Abs(robotY - oldRobotX) > distanceThreshold)
            {
                oldRobotX = robotX;
                oldRobotY = robotY;

                pathArray.Add(new Point(robotX,robotY));
            }
            int l = pathArray.Count;
            for (int i = 1; i < l; i++)
            {
                g.DrawLine(pathPen, pathArray[i - 1].X, pathArray[i - 1].Y, pathArray[i].X, pathArray[i].Y);
            }

        }

        private void drawObstacle(Graphics g)
        {
            if (World.ObstacleMap != null)
            {
                int obstacleX = (int)(zeroPointX + World.ObstacleMap.Area.X * gridXSize);
                int obstacleY = (int)(zeroPointY + World.ObstacleMap.Area.Y * gridYSize);
                g.DrawImage(World.ObstacleMap.Image, new Rectangle(obstacleX, obstacleY, (int)(World.ObstacleMap.Area.Width*gridXSize), (int)(World.ObstacleMap.Area.Height*gridYSize)), new Rectangle(0, 0, 900, 900), GraphicsUnit.Pixel);
            }
        }
        private void drawGrid(Graphics g)
        {
            Pen currentPen;

            for (int i = 0; i <= world.GridX; i++)
            {
                currentPen = (i == world.ZeroX) ? zeroPen : normalPen;
                g.DrawLine(currentPen, i * gridXSize, 0, i * gridXSize, world.GridY*gridYSize);

            }
            for (int i = 0; i <= world.GridY; i++)
            {
                currentPen = (i == world.ZeroY) ? zeroPen : normalPen;
                g.DrawLine(currentPen, 0, i * gridYSize, world.GridX*gridXSize, i*gridYSize);

            }
        }
    }
}
